package com.dux.commons.network;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.channels.SelectionKey;
import java.nio.channels.SocketChannel;


/**
 * 客户端连接到服务器产生的套接字
 * @author 杜祥
 * @create 2013-6-1
 */
public abstract class MMOConnection 
{
	
	/**
	 * 这个连接的通道
	 */
	private final SocketChannel socketChannel;
	
	
	/**
	 * 调度器
	 */
	private final Dispatcher dispatcher;
	
	
	/**
	 * 这个连接的选择键
	 */
	private SelectionKey		key;
	
	/**
	 * 为true表示将发送最后一个数据包，然后触发onDisconnection方法，关闭连接。
	 */
	protected boolean pendingClose;
	
	/**
	 * 为true，直接触发onDisconnection方法，关闭连接。
	 */
	protected boolean isForcedClosing;
	
	/**
	 * 为true，则表明这个连接已经关闭。
	 */
	protected boolean closed;
	
	
	/**
	 * 连接的同步对象
	 */
	protected final Object guard = new Object();
	
	/**
	 * 读数据的字节缓冲流
	 */
	final ByteBuffer readBuffer;
	
	/**
	 * 写数据的字节缓冲流
	 */
	final ByteBuffer writeBuffer;
	
	/**
	 * 缓存IP地址，以确保即使断开后{@link #getIp()}方法
	 */
	private String ip;
	
	
	/**
	 * 用于包处理同步。
	 */
	private boolean locked = Boolean.FALSE;
	
	
	/**
	 * 构造一个新的连接实例，并初始化连接相关数据
	 * @param sc
	 * 					套接字通道
	 * @param d
	 * 					调度器
	 * @throws IOException 
	 */
	public MMOConnection(SocketChannel sc, Dispatcher d) throws IOException
	{
		this.socketChannel = sc;
		this.dispatcher = d;
		
		this.writeBuffer = ByteBuffer.allocate(20 * 1024);
		this.writeBuffer.order(ByteOrder.LITTLE_ENDIAN);
		this.writeBuffer.flip();
		this.readBuffer = ByteBuffer.allocate(10 * 1024);
		this.readBuffer.order(ByteOrder.LITTLE_ENDIAN);
		
		this.ip = socketChannel.socket().getInetAddress().getHostAddress();
	}
	
	
	
	/**
	 * 设置选择键
	 * @param key
	 */
	final void setKey(SelectionKey key)
	{
		this.key = key;
	}
	
	
	/**
	 * 通知调度器连接写入数据
	 */
	protected final void enableWriteInterest()
	{
		if(this.key.isValid())
		{
			this.key.interestOps(this.key.interestOps() | SelectionKey.OP_WRITE);
			this.key.selector().wakeup();
		}
	}
	
	/**
	 * 获得此连接使用的调度器
	 * @return
	 */
	final Dispatcher getDispatcher() 
	{
		return dispatcher;
	}
	
	
	/**
	 * 获得此连接的套接字通道
	 * @return
	 */
	public SocketChannel getSocketChannel() 
	{
		return socketChannel;
	}
	
	
	/**
	 * 连接将被关闭一段时间，然后触发调用onDisconnect()方法，以清除连接其他所有操作。
	 * @param forced
	 */
	public final void close(boolean forced)
	{
		synchronized (guard)
		{
			if(isWriteDisabled())
				return;
			
			isForcedClosing = forced;
			dispatcher.closeConnection(this);
		}
	}
	
	
	/**
	 * 这个方法将不关心其它操作，强行关闭连接
	 * @return  如果这个连接之前没有被关闭则返回true，否则false
	 */
	final boolean onlyClose()
	{
		synchronized (guard) 
		{
			if(closed)
				return Boolean.FALSE;
			try
			{
				if(socketChannel.isOpen())
				{
					socketChannel.close();
					key.attach(null);
					key.cancel();
				}
				closed = Boolean.TRUE;
			} 
			catch (IOException e) 
			{
			}
			
		}
		return Boolean.TRUE;
	}
	
	/**
	 * 仅用于包处理，如果之前没有锁定过在锁定成功以后则返回true，否则false。
	 * @return
	 */
	boolean tryLoackConnection()
	{
		if(locked)
			return Boolean.FALSE;
		
		return locked = Boolean.TRUE;
	}
	
	
	/**
	 * 仅用于包处理，释放锁。
	 */
	void unLockConnection()
	{
		locked = Boolean.FALSE;
	}
	
	
	/**
	 * 如果此连接正在等待关闭，还没有关闭则返回True
	 * @return
	 */
	final boolean isPendingClose()
	{
		return pendingClose && !closed;
	}
	
	
	/**
	 * 返回true则表明这个连接是不可写的。
	 * @return
	 */
	protected final boolean isWriteDisabled()
	{
		return pendingClose || closed;
	}


	public String getIp() 
	{
		return ip;
	}
	
	
	/**
	 * 此方法将被通信调度器使用，用于接收到客户端数据包的处理。
	 * @param data
	 * @return
	 */
	protected abstract boolean processData(ByteBuffer data);
	
	
	/**
	 * 此方法将被调度器使用，并且将重复执行，直到返回false
	 * @param data
	 * @return			
	 */
	protected abstract boolean writeData(ByteBuffer data);
	
	
	/**
	 * 这个方法在连接关闭的时候会被调用，此方法只调用一次。
	 */
	abstract protected void onDisconnect();
	
	
	/**
	 * 获得断开连接延迟时间
	 * @return
	 */
	protected abstract long getDisconnectionDelay();
	
	
	/**
	 * 关闭服务器调用方法
	 */
	protected abstract void onServerClose();
	
	
	/**
	 * 初始化方法
	 */
	abstract protected void initialized();
	
}
